/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief SQL data container header.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: SQLResult.hpp,v 1.6 2009-08-11 08:03:43 amaula Exp $
 *
 */
#ifndef _MACI_SQL_SQLRESULT_HPP_
#define _MACI_SQL_SQLRESULT_HPP_
#include "SQLResultTypes.hpp"
#include <string>
#include <vector>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <assert.h>

// Result version string constant.
#define MACI_SQL_SQLRESULT_VERSION_STRING "0.5"

namespace MaCI {
  namespace SQL {
    
    /** Container datatype.
     *
     * This result container can carry the following types:
     */
    enum EResultType {
      KUnknown,           ///< Result set type is currently unassigned/unknown
      KTable,             ///< Result contains a table.
      KError,             ///< Result contains an error.
      KAffectedRows       ///< Result contains affected rows count.
    };


    /** SQL Result encapsulation class.
     *
     * This class is intended for handling SQL result data transfer
     * between GIMnet nodes. Its interface is capable of storing and
     * returning SQL table data, while keeping it in compatible XML
     * format while in transit. The storage format XML is designed to
     * be as small and fast-to-interpret as possible to keep the
     * overhead of transferring over GIMnet in minimum.
     *
     * This class is designed to act as base-class for application
     * specific SQL result interpretations.
     *
     * \todo Define properly to be used as base-class for application
     * specific SQL result interpreter. :)
     *
     */
    class CSQLResult 
    {
    public:
      /** Default empty constructor.
       *
       */
      CSQLResult(void);
      
      
      /** Constructor to decode a XML datablob.
       * 
       * @param[in] aXMLData    XML data as std::string to decode as data in SQLResult format.
       */
      CSQLResult(const std::string &aXMLData);
      
      
      /** Destructor.
       *
       */
      virtual ~CSQLResult();


      /** Reset result.
       * 
       * This function empties all stored data; making the result
       * as it would have just been constructed.
       */
      void Reset(void);

      
      /** Encode current class contents to XML string.
       *
       * This funtion converts the current memory representation of
       * SQL data to XML format. The XML string is stored on given
       * std::string reference.
       * 
       * @param[out]            Converted XML data.
       * @return                'true' on succesfully converted, 'false'
       *                        on any error.
       *
       */
      bool EncodeSQLResultXML(std::string &aXMLData) const;


      /** Encode current class contents to internal XML result.
       *
       * This function converts the current memory representation of
       * SQL data to XML format. The XML string is stored in internal
       * XML string data result. Stored data can be fetched anytime
       * by calling GetXMLData() function.
       *
       * @return                'true' when conversion was succesfull,
       *                        'false' on any error.
       */
      bool EncodeSQLResultXML(void);

      
      /** Decode a XML datastring containing SQLResult compatible XML.
       *
       * @param[in] aXMLData    XML data to decode as SQLResult compatible XML.
       * @return                'true' when given data was succesfully decoded as
       *                        SQLResult compatible XML. 'false' on any error.
       */
      bool DecodeSQLResultXML(const std::string &aXMLData);


      /** Return reference to internal XMLdata-result std::tring.
       * 
       * @return                Const reference to internal XMLData string.
       */
      const std::string &GetXMLData(void) const { return iXMLData; }


      /** Return reference to Table data.
       *
       */
      const CSQLTable &GetTable(void) const {
        return iTable;
      }


      /** Returns the last error string.
       *
       * If this is an error result, the returned error string will be
       * non-zero length.  Also, error may be checked by calling
       * 'GotError()' function.
       * 
       * @return                Reference to error string inside container.
       */
      const std::string &GetLastError(void) const {
        return iLastError;
      }


      /** Get number of affected rows.
       *
       * If the result is AffectedRows, returns the value stored; if
       * the type is not AffectedRows, returns -1.
       *
       * @return                Number of affected rows, or negative if
       *                        result doesn't carry 'AffectedRows'
       *                        information.
       */
      const long long int GetAffectedRows(void) const {
        return (iResultType == KAffectedRows) ? iAffectedRows : -1;
      }


      /** Set Table. 
       *
       * This function assigns a new table to this result.
       *
       * \note This operation wipes out the possibly stored XML data.
       * \note This sets the container type to Table.
       *
       * @param[in] aTable      Table data to assign.
       */
      void SetTable(const CSQLTable &aTable) {
        iXMLData.clear();
        iTable = aTable;
        iResultType = KTable;
      }


      /** Set error.
       *
       * This function assigns an error string in the container. If
       * any other fields are set, they are wiped out. Error string
       * must have a non-zero length, but contents are free (error is
       * mostly user interpreted if any)
       *
       * \note This operation wipes out the possibly stored XML data.
       * \note This sets the container type to Error.
       * 
       * @param[in] aErrorString Non-zero length error string
       *                         describing the error.
       */
      void SetError(const std::string &aErrorString) {
        iXMLData.clear();
        iLastError = aErrorString;
        iResultType = KError;
      }

      
      /** Set affected rows count.
       *
       * Container is set to carry 'affected rows' information, which
       * means: "the number of rows changed (for UPDATE), deleted (for
       * DELETE), or inserted (for INSERT) rows"
       *
       * \note This operation wipes out the possibly stored XML data.
       * \note This sets the container type to AffectedRows
       *
       * @param[in] aAffectedRows Number of affected rows. Can also be
       *                          zero. Negative values should not occure,
       *                          as it means an error, and should be
       *                          handled by the Error type.
       */
      void SetAffectedRows(const long long int &aAffectedRows) {
        iXMLData.clear();
        iAffectedRows = aAffectedRows;
        iResultType = KAffectedRows;
      }
      

      /** Returns true if the container is a Table container.
       *
       * If the container contains a valid SQL result table, this
       * function will return 'true'. Note that table is valid even if
       * its empty - query was OK, but yielded no results.
       *
       * @return                'true' if the container contains a
       *                        valid SQL result table.
       */
      bool GotTable(void) const {
        return iResultType == KTable;
      }


      /** Returns true if the container is Error container.
       *
       * If the container is error container, the container doesn't
       * contain Table data, instead calling GetLastError() will yield
       * an error string from the SQL implementation.
       *
       * @return                'true' if the result contains an error reply.
       */
      bool GotError(void) const {
        return iResultType == KError;
      }

      
      /** Returns true if the container is a AffectedRows container.
       *
       * If the container is a Affected Rows container, this function
       * will return true.
       *
       * @return                'true' if the result contains affected rows entry.
       */
      bool GotAffectedRows(void) const {
        return iResultType == KAffectedRows;
      }


      /** Return container type.
       *
       * This function returns the type of the container. Container
       * can be one of the types defined by type EResultType.
       *
       * @return                Container type.
       */
      EResultType GetResultType(void) const {
        return iResultType;
      }

    private:
      bool DecodeTagAffected(xmlDocPtr docp, xmlNodePtr nodep);
      bool DecodeTagError(xmlDocPtr docp, xmlNodePtr nodep);
      bool DecodeTagTable(xmlDocPtr docp, xmlNodePtr nodep);
      bool DecodeTagRow(xmlDocPtr docp, xmlNodePtr nodep, CSQLTable &table);
      bool DecodeTagField(xmlDocPtr docp, xmlNodePtr nodep, TSQLRow &row);


      bool EncodeTagField(std::string &aXML, const CSQLTable &t, 
                          const unsigned int aRow, 
                          const unsigned int aColumn) const;
      bool EncodeTagRow(std::string &aXML, const CSQLTable &t, const unsigned int aRow) const;
      bool EncodeTagTable(std::string &aXML, const CSQLTable &t) const;
      bool EncodeTagError(std::string &aXML, const std::string &aError) const;
      bool EncodeTagAffected(std::string &aXML, const long long int &aAffected) const;

    private: // XML data
      std::string iXMLData;

    private: // data
      EResultType iResultType;     ///< Container result type.
      CSQLTable iTable;            ///< Table data container.
      std::string iLastError;      ///< Last error.
      long long int iAffectedRows; ///< Affected rows.

    private: // statics
      static std::string iResultVersion;
    };
  }
}

#endif
